package com.search.service.impl;

import com.alibaba.fastjson2.JSON;
import com.common.constant.ESProperties;
import com.common.constant.RedisKey;
import com.common.domain.vo.SearchQueryByKeywordResultVO;
import com.common.domain.vo.SearchQueryByKeywordVO;
import com.search.service.SearchService;
import lombok.RequiredArgsConstructor;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.lucene.search.function.FieldValueFactorFunction;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * SearchServiceImpl类实现了SearchService接口，提供基于Elasticsearch的搜索功能
 * 主要负责处理根据关键词查询视频信息的逻辑
 */
@Service
@RequiredArgsConstructor
public class SearchServiceImpl implements SearchService {

    private final ESProperties esProperties;

    private final ThreadPoolTaskExecutor taskExecutor;

    private final StringRedisTemplate stringRedisTemplate;

    /**
     * 根据关键词查询视频信息
     *
     * @param keyword 关键词，用于匹配视频名称
     * @param page 页码，用于分页查询
     * @return 返回包含查询结果的SearchQueryByKeywordResultVO对象
     */
    @Override
    public SearchQueryByKeywordResultVO queryByKeyword(String keyword, Integer page) {
        // 1.创建连接
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost(esProperties.getHost(), esProperties.getPort(), "http"))
        );
        // 创建查询请求
        SearchRequest request = new SearchRequest("video");
        // 创建查询源构建器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        // 构建函数得分查询，匹配关键词并根据关注数排序
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(QueryBuilders.matchQuery("name", keyword),
                        ScoreFunctionBuilders
                                .fieldValueFactorFunction("followCount")
                                .factor(1)
                                .missing(1)
                                .modifier(FieldValueFactorFunction.Modifier.NONE))
                .boostMode(CombineFunction.SUM);
        FunctionScoreQueryBuilder prefixQueryFunctionScoreQueryBuilder = QueryBuilders.functionScoreQuery(QueryBuilders.prefixQuery("shortName", keyword),
                ScoreFunctionBuilders
                        .fieldValueFactorFunction("followCount")
                        .factor(1)
                        .missing(1)
                        .modifier(FieldValueFactorFunction.Modifier.NONE))
                .boostMode(CombineFunction.SUM);
        // 设置查询条件
        builder.query(QueryBuilders.boolQuery()
                .should(functionScoreQueryBuilder)
                // 补充前缀查询
                .should(prefixQueryFunctionScoreQueryBuilder));
        return getSearchQueryByKeywordResultVO(client, request, builder, page);
    }

    /**
     * 根据标签查询视频信息
     *
     * @param tags 标签，用于匹配视频标签
     * @param page 页码，用于分页查询
     * @return 返回包含查询结果的SearchQueryByKeywordResultVO对象
     */
    @Override
    public SearchQueryByKeywordResultVO queryByTags(String tags, Integer page) {
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost(esProperties.getHost(), esProperties.getPort(), "http"))
        );
        SearchRequest request = new SearchRequest("video");
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(QueryBuilders.matchQuery("tags", tags));
        return getSearchQueryByKeywordResultVO(client, request, builder, page);
    }

    /**
     * 根据演员或 actresses 的姓名和性别查询视频信息
     *
     * @param name   演员或 actresses 的姓名
     * @param gender 性别，1 代表男演员，0 代表女演员
     * @param page   页码，用于分页查询
     * @return 返回查询结果对象
     */
    @Override
    public SearchQueryByKeywordResultVO queryByActor(String name, Integer gender, Integer page) {
        // 创建 Elasticsearch 客户端
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost(esProperties.getHost(), esProperties.getPort(), "http"))
        );
        // 创建查询请求，指定索引名为 "video"
        SearchRequest request = new SearchRequest("video");
        // 创建查询构建器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        // 根据性别参数决定查询字段
        if (gender == 1){
            // 如果是男演员，查询 "actor" 字段
            builder.query(QueryBuilders.termQuery("actor", name));
        }else if (gender == 0){
            // 如果是女演员，查询 "actress" 字段
            builder.query(QueryBuilders.termQuery("actress", name));
        }
        // 调用方法执行查询并返回结果
        return getSearchQueryByKeywordResultVO(client, request, builder, page);
    }

    /**
     * 执行搜索查询并构建结果对象
     *
     * @param client Elasticsearch客户端
     * @param request 搜索请求
     * @param builder 查询源构建器
     * @param page 页码
     * @return 返回SearchQueryByKeywordResultVO对象，包含查询结果
     */
    private SearchQueryByKeywordResultVO getSearchQueryByKeywordResultVO(RestHighLevelClient client, SearchRequest request, SearchSourceBuilder builder, Integer page) {
        // 设置分页参数
        Integer startIndex = (page - 1) * 10;
        builder.from(startIndex);
        builder.size(10);
        request.source(builder);
        SearchResponse response = null;
        try {
            response = client.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 处理查询结果
        SearchHits hits = response.getHits();
        long total = hits.getTotalHits().value;
        SearchHit[] hitsHits = hits.getHits();
        List<SearchQueryByKeywordVO> list = new ArrayList<>();
        for (SearchHit hitsHit : hitsHits) {
            SearchQueryByKeywordVO searchQueryByKeywordVO = JSON.parseObject(hitsHit.getSourceAsString(), SearchQueryByKeywordVO.class);
            list.add(searchQueryByKeywordVO);
        }
        // 补充：查询一次，就给redis里的查询次数加一
        taskExecutor.execute(() -> {
            for (SearchQueryByKeywordVO searchQueryByKeywordVO : list) {
                String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
                stringRedisTemplate.opsForHash().increment(RedisKey.LIST_SEARCH_KEY + format, searchQueryByKeywordVO.getId().toString(), 1);
            }
        });
        return new SearchQueryByKeywordResultVO(total, page.longValue(), list);
    }
}
